<?php

namespace app;

use app\common\Constants;
use app\common\ErrorCode;
use app\common\exception\BusinessException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\facade\Lang;
use think\facade\Log;
use think\Response;
use Throwable;

/**
 * 应用异常处理类
 */
class ExceptionHandle extends Handle
{
    /**
     * 不需要记录信息（日志）的异常类列表
     * @var array
     */
    protected $ignoreReport = [
        HttpException::class,
        HttpResponseException::class,
        ModelNotFoundException::class,
        DataNotFoundException::class,
        ValidateException::class,
        BusinessException::class,
    ];

    /**
     * 记录异常信息（包括日志或者其它方式记录）
     *
     * @access public
     * @param Throwable $exception
     * @return void
     */
    public function report(Throwable $exception): void
    {
        $data = [
            'code' => $exception->getCode(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'message' => $exception->getMessage(),
        ];

        // 当前在Debug模式 或者 不是忽略列表中的异常
        if ($this->app->isDebug() || !$this->isIgnoreReport($exception)) {
            trace($data, 'debug');
        }

        // 记录异常信息到管理系统
        if (config('rds.exception_save_db')) {
            event('AdminLog', [
                'type' => Constants::LOG_TYPE_EXCEPTION,
                'content' => $exception->getMessage()
            ]);
        }

        parent::report($exception);
    }


    /**
     * Render an exception into an HTTP response.
     *
     * @access public
     * @param \think\Request $request
     * @param Throwable $e
     * @return Response
     */
    public function render($request, Throwable $e): Response
    {
        trace('通用异常------------------------------');

        $response = [
            'success' => false,
            'code' => ErrorCode::INTERNAL_SERVER_ERROR, // 默认系统错误码
            'message' => $this->translateMessage(Constants::E_SYS_SERVER_INTERNAL_ERROR), // 默认错误信息:服务器内部错误，请联系管理员
            'data' => [],
        ];

        // 验证错误
        if ($e instanceof ValidateException) {
            trace('匹配了 ValidateException 异常');
            // 如果是 Debug 模式，则返回实际错误信息，如果不是 Debug 则返回数据验证失败
            $msgKey = $this->app->isDebug() ? $e->getMessage() : Constants::E_COMMON_DATA_VALIDATION_FAILED;
            $response['code'] = ErrorCode::UNPROCESSABLE_ENTITY;
            $response['message'] = $this->translateMessage($msgKey);
            return json($response, ErrorCode::UNPROCESSABLE_ENTITY);
        }

        // 模型异常
        if ($e instanceof ModelNotFoundException || $e instanceof DataNotFoundException) {
            trace('匹配了 ModelNotFoundException 异常');
            // 如果是 Debug 模式，则返回实际错误信息，如果不是 Debug 则返回数据库服务繁忙，请稍后再试
            $msgKey = $this->app->isDebug() ? $e->getMessage() : Constants::E_SYS_DATABASE_ERROR;
            $response['code'] = ErrorCode::NOT_FOUND;
            $response['message'] = $this->translateMessage($msgKey);
            return json($response, ErrorCode::NOT_FOUND);
        }

        //
        if ($e instanceof DbException) {
            trace('匹配了 DbException 异常');
            // 如果是 Debug 模式，则返回实际错误信息，如果不是 Debug 则返回数据库服务繁忙，请稍后再试
            $msgKey = $this->app->isDebug() ? $e->getMessage() : Constants::E_SYS_DATABASE_ERROR;
            $response['message'] = $this->translateMessage($msgKey);
            return json($response, ErrorCode::INTERNAL_SERVER_ERROR);
        }

        //
        if ($e instanceof \RuntimeException) {
            trace('匹配了 RuntimeException 异常');
            // 如果是 Debug 模式，则返回 实际错误信息，如果不是 Debug 则返回 系统运行时出现错误
            $msgKey = $this->app->isDebug() ? $e->getMessage() : Constants::E_SYS_RUNTIME_EXCEPTION;
            $response['message'] = $this->translateMessage($msgKey);
            return json($response, ErrorCode::INTERNAL_SERVER_ERROR);
        }

        // 业务逻辑异常
        if ($e instanceof \LogicException) {
            trace('匹配了 LogicException 异常');
            // 如果是 Debug 模式，则返回 实际错误信息，如果不是 Debug 则返回 请求参数不合法
            $msgKey = $this->app->isDebug() ? $e->getMessage() : Constants::E_COMMON_INVALID_PARAM;
            $response['message'] = $this->translateMessage($msgKey);
            return json($response, ErrorCode::INTERNAL_SERVER_ERROR);
        }

        // 业务异常
        if ($e instanceof BusinessException) {
            trace('匹配了 BusinessException 异常');
            // 业务异常，状态码正常，返回的消息包含业务消息和业务代码
            $response['success'] = true;
            $response['code'] = $e->getCode();
            $response['message'] = $this->translateMessage($e->getMessage());
            return json($response, ErrorCode::SUCCESS);
        }

        //
        if ($this->app->isDebug()) {
            trace('未匹配特定异常，Debug模式下交由父类处理');
            return parent::render($request, $e);
        }

        Log::error('未匹配特定异常，生产环境下返回通用错误响应');
        return json([
            'success' => false,
            'code' => ErrorCode::INTERNAL_SERVER_ERROR,
            'message' => $this->translateMessage(Constants::E_SYS_SERVER_INTERNAL_ERROR), // "服务器内部错误，请联系管理员"
            'data' => [],
        ], ErrorCode::INTERNAL_SERVER_ERROR); // HTTP 状态码也设为 500

    }


    /**
     * 统一翻译消息的辅助方法
     *
     * @param string $msg 原始消息或语言包键名
     * @return string 翻译后的消息或原消息
     */
    private function translateMessage(string $msg): string
    {
        // 使用 Lang::has() 检查是否存在对应的语言定义
        if (Lang::has($msg)) {
            // 如果存在，则使用 Lang::get() 进行翻译
            return Lang::get($msg);
        }
        // 如果不存在，则原样返回
        return $msg;
    }
}
